package server

import "fmt"

//import "log"

//type Card struct {
//	Number    uint8 // 牌值
//	Varieties uint8 // 花色
//}

const (
	Highcard      = iota
	Pair           // 1
	TwoPairs       // 2
	ThreeofAKind   // 3
	Straight       // 4
	xFlush         // 5
	Fullhouse      // 6
	FourofAKind    // 7
	StraightFlush  // 8
	RoyalFlush     // 9
)

const (
	spade   = 0x01 << 0
	heart   = 0x01 << 1
	club    = 0x01 << 2
	diamond = 0x01 << 3
)

/* Exported function */
func Pokervalue(inputArray []Card) (uint, []Card) {
	fmtCard := make([]uint8, 14)
	formatArray(inputArray, fmtCard)
	fmt.Println(fmtCard)

	r, keyCards := CheckResult(fmtCard)
	fmt.Println("result:", r)
	fmt.Println(keyCards)

	ret, outPut := convtArray(inputArray, r, keyCards)
	fmt.Println("=>", ret)
	fmt.Println("=>", outPut)
	return ret, outPut
}

func formatArray(inputArray []Card, fmtCard []uint8) {
	// Treat all ACE both as 1 and 14
	for i := 0; i < len(inputArray); i++ {
		fmtCard[14-inputArray[i].Number] |= 0x01 << inputArray[i].Varieties
		if inputArray[i].Number == 1 {
			fmtCard[0] = fmtCard[13]
		}
	}
}

func convtArray(c []Card, r uint, keyCards []uint8) (uint, []Card) {
	var j uint8 = 0
	var retCards = make([]Card, 0, 5)

	for i := 0; i < len(c); i++ {
		if c[i].Number == 1 {
			c[i].Number = 14
		}
	}
	for i := 0; i < len(c)-1; i++ {
		for j := 0; j < len(c)-1-i; j++ {
			if c[j].Number < c[j+1].Number {
				c[j+1], c[j] = c[j], c[j+1]
			}
		}

	}
	for i := 0; i < len(c); i++ {
		if c[i].Number == 14 {
			c[i].Number = 1
		}
	}

	//fmt.Println(c)
	//fmt.Println(keyCards)

	for i := 0; i < len(keyCards); i++ {
		if keyCards[i] != 0 {
			appValue := 14 - uint8(i)
			if i == 0 {
				appValue = 1
			}

			if keyCards[i]&spade != 0 {
				retCards = append(retCards, Card{appValue, 0})
			}
			if keyCards[i]&heart != 0 {
				retCards = append(retCards, Card{appValue, 1})
			}
			if keyCards[i]&club != 0 {
				retCards = append(retCards, Card{appValue, 2})
			}
			if keyCards[i]&diamond != 0 {
				retCards = append(retCards, Card{appValue, 3})
			}
		}
	}
	//fmt.Println(retCards)

	switch r {
	case Highcard:
		/* Append all 5 */
		retCards = append(retCards, c[0], c[1], c[2], c[3], c[4])
	case Pair:
		/* Append 3 more */
		var i Card
		for _, i = range c {
			if i.Number != retCards[0].Number {
				retCards = append(retCards, i)
				j ++
				if j == 3 {
					break
				}
			}

		}
	case ThreeofAKind:
		/* Append 2 more */
		var i Card
		for _, i = range c {
			if i.Number != retCards[0].Number {
				retCards = append(retCards, i)
				j ++
				if j == 2 {
					break
				}
			}

		}
	case TwoPairs, FourofAKind:
		/* Append 1 more */
		var i Card
		for _, i = range c {
			if i.Number != retCards[0].Number && i.Number != retCards[2].Number {
				retCards = append(retCards, i)
				break
			}

		}
	case Straight, xFlush, Fullhouse, StraightFlush, RoyalFlush:
		/* Do nothing */
	default:
		/* Do nothing */
	}

	return r, retCards
}

/*
 * output:
 * @ type: which type is, highcard, pair, etc ...
 * @ keycards
 */
func CheckResult(fmtCard []uint8) (uint, []uint8) {
	var spadeNum, heartNum, clubNum, diamondNum uint8
	// number of flush attempt and least value
	var straightValue = [2]uint8{0, 0}
	// number of apprearing times and value
	var pairValue = [4]uint8{0, 0, 0, 0}
	// flush info
	var bFlush bool = false
	var varMask uint8

	cardNum := make([]uint8, 14)
	retCard := make([]uint8, 14)

	for i := 0; i < len(fmtCard); i++ {
		ones := fmtCard[i]

		// calculate the number of ones
		ones = (ones & 0x05) + (ones >> 1 & 0x05)
		ones = (ones & 0x03) + (ones >> 2 & 0x03)
		cardNum[i] = ones

		if cardNum[i] >= 2 && i != len(fmtCard)-1 {
			if pairValue[0] == 0 {
				pairValue[0] = cardNum[i]
				pairValue[1] = 13 - uint8(i)
			} else {
				if cardNum[i] > pairValue[0] {
					pairValue[2] = pairValue[0]
					pairValue[3] = pairValue[1]
					pairValue[0] = cardNum[i]
					pairValue[1] = 13 - uint8(i)
				} else {
					if pairValue[2] == 0 {
						pairValue[2] = cardNum[i]
						pairValue[3] = 13 - uint8(i)
					}
				}
			}
		}

		if fmtCard[i] != 0 {
			if i != 13 {
				if fmtCard[i]&spade != 0 {
					spadeNum ++
				}
				if fmtCard[i]&heart != 0 {
					heartNum ++
				}
				if fmtCard[i]&club != 0 {
					clubNum ++
				}
				if fmtCard[i]&diamond != 0 {
					diamondNum ++
				}
			}
			if straightValue[0] < 5 {
				straightValue[0] ++
				if straightValue[1] == 0 {
					straightValue[1] = 13 - uint8(i)
				}
			}
		} else {
			if straightValue[0] < 5 {
				straightValue[0] = 0
				straightValue[1] = 0
			}
		}
	}

	// value conclusion
	//RoyalFlush     // 9
	//StraightFlush  // 8
	if (spadeNum >= 5) || (heartNum >= 5) || (clubNum >= 5) || (diamondNum >= 5) {
		var flushStraight = [2]uint8{0, 0}
		bFlush = true
		if spadeNum >= 5 {
			varMask = spade
		} else if heartNum >= 5 {
			varMask = heart
		} else if clubNum >= 5 {
			varMask = club
		} else if diamondNum >= 5 {
			varMask = diamond
		}

		for i := 0; i < len(fmtCard); i++ {
			retCard[i] = fmtCard[i] & varMask
		}

		for i := 0; i < len(retCard); i++ {
			if retCard[i] != 0 {
				if flushStraight[0] < 5 {
					flushStraight[0] ++
					if flushStraight[1] == 0 {
						flushStraight[1] = 13 - uint8(i)
					}
				}
			} else {
				if flushStraight[0] < 5 {
					flushStraight[0] = 0
					flushStraight[1] = 0
				}
			}
		}

		if flushStraight[0] >= 5 {
			for i := 0; i < int(13-flushStraight[1]); i++ {
				retCard[i] = 0
			}

			for i := int(13 - flushStraight[1] + 5); i < len(fmtCard); i++ {
				retCard[i] = 0
			}

			if flushStraight[1] == 13 {
				return RoyalFlush, retCard
			} else {
				return StraightFlush, retCard
			}
		}
	}

	//FourofAKind    // 7
	if pairValue[0] == 4 {
		retCard[13-pairValue[1]] = fmtCard[13-pairValue[1]]
		return FourofAKind, retCard
	}

	//Fullhouse      // 6
	if (pairValue[0] == 3) && (pairValue[2] >= 2) {
		retCard[13-pairValue[1]] = fmtCard[13-pairValue[1]]
		retCard[13-pairValue[3]] = fmtCard[13-pairValue[3]]
		return Fullhouse, retCard
	}

	//xFlush          // 5
	if bFlush {
		var j int = 0
		for i := 0; i < len(retCard); i++ {
			if retCard[i] != 0 {
				j++
				if j > 5 {
					retCard[i] = 0
				}
			}
		}
		return xFlush, retCard
	}

	//Straight       // 4
	if straightValue[0] >= 5 {
		for i := 0; i < 5; i++ {
			retCard[13-int(straightValue[1])+i] = fmtCard[13-int(straightValue[1])+i]
		}
		return Straight, retCard
	}

	//ThreeofAKind   // 3
	if pairValue[0] == 3 {
		retCard[13-pairValue[1]] = fmtCard[13-pairValue[1]]
		return ThreeofAKind, retCard
	}

	//TwoPairs       // 2
	if (pairValue[0] == 2) && (pairValue[2] == 2) {
		retCard[13-pairValue[1]] = fmtCard[13-pairValue[1]]
		retCard[13-pairValue[3]] = fmtCard[13-pairValue[3]]

		return TwoPairs, retCard
	}

	// Pair           // 1
	if pairValue[0] == 2 {
		retCard[13-pairValue[1]] = fmtCard[13-pairValue[1]]
		return Pair, retCard
	}

	// Highcard
	return Highcard, retCard
}
